/*
 * Project: book
 * 
 * File Created at 2017年9月27日
 * 
 * Copyright 2016 CMCC Corporation Limited.
 * All rights reserved.
 *
 * This software is the confidential and proprietary information of
 * ZYHY Company. ("Confidential Information").  You shall not
 * disclose such Confidential Information and shall use it only in
 * accordance with the terms of the license.
 */
package com.whb.book.common.util;
import java.io.BufferedInputStream;
import java.io.BufferedReader;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.io.OutputStream;
import java.net.URLEncoder;
import java.util.ArrayList;
import java.util.Iterator;
import java.util.List;
import java.util.Vector;
import javax.servlet.http.HttpServletResponse;

import com.jcraft.jsch.Channel;
import com.jcraft.jsch.ChannelSftp;
import com.jcraft.jsch.ChannelSftp.LsEntry;
import com.jcraft.jsch.JSch;
import com.jcraft.jsch.Session;
import com.jcraft.jsch.SftpATTRS;
import com.jcraft.jsch.SftpException;

/**
 * @Type SFTPUtil.java
 * @Desc 
 * @author wanghb
 * @date 2017年9月27日 下午7:12:01
 * @version 
 */
public class SFTPUtil {

    public static final String CHARSET = "utf-8";
    public static final String PROPERTY = System.getProperty("line.separator");

    private String host;//服务器连接ip
    private String username;//用户名
    private String password;//密码
    private int port = 22;//端口号
    private ChannelSftp sftp = null;
    private Session sshSession = null;

    public SFTPUtil(String host, String username, String password, int port) {
        super();
        this.host = host;
        this.username = username;
        this.password = password;
        this.port = port;
    }

    public SFTPUtil(String host, String username, String password) {
        super();
        this.host = host;
        this.username = username;
        this.password = password;
    }

    /**
     * 连接sftp
     */
    public void connect() {
        JSch jsch = new JSch();
        try {
            sshSession = jsch.getSession(username, host, port);
            sshSession.setPassword(password);
            sshSession.setConfig("StrictHostKeyChecking", "no");
            sshSession.connect();
            Channel channel = sshSession.openChannel("sftp");
            channel.connect();
            sftp = (ChannelSftp) channel;
        } catch (Exception e) {
            throw new RuntimeException("连接SFTP失败");
        }
    }

    /**
     * 断开sftp
     */
    public void disconnect() {
        if (this.sftp != null && this.sftp.isConnected()) {
            this.sftp.disconnect();
        }
        if (this.sshSession != null && this.sshSession.isConnected()) {
            this.sshSession.disconnect();
        }
    }

    /**
     * 下载文件到本地
     *
     * @author whb
     * @param remotePath
     * @param remoteFileName
     * @param localPath
     * @param localName
     * @return
     * @since JDK 1.8
     */
    public void downloadFile(String remotePath, String remoteFileName, String localPath,
                             String localName) {
        try (InputStream is = sftp.get(remotePath + remoteFileName);
                FileOutputStream fs = new FileOutputStream(new File(localPath + localName))) {
            StringBuffer sb = new StringBuffer();
            String buffer = "";
            BufferedReader reader = new BufferedReader(new InputStreamReader(is));
            while ((buffer = reader.readLine()) != null) {
                sb.append(buffer + PROPERTY);
            }
            fs.write(sb.toString().getBytes(CHARSET));
        } catch (SftpException | IOException e) {
            throw new RuntimeException("下载文件出错");
        }
    }

    /**
     * 下载文件到本地
     *
     * @author whb
     * @param remotePath
     * @param remoteFileName
     * @param localPath
     * @param localName
     * @return 返回boolean类型
     * @since JDK 1.8
     */
    public boolean downloadFileBoolean(String remotePath, String remoteFileName, String localPath,
                                       String localName) {
        try (InputStream is = sftp.get(remotePath + remoteFileName);
                FileOutputStream fs = new FileOutputStream(new File(localPath + localName))) {
            StringBuffer sb = new StringBuffer();
            String buffer = "";
            BufferedReader reader = new BufferedReader(new InputStreamReader(is));
            while ((buffer = reader.readLine()) != null) {
                sb.append(buffer + PROPERTY);
            }
            fs.write(sb.toString().getBytes(CHARSET));
            return true;
        } catch (SftpException | IOException e) {
            return false;
        }
    }

    /**
     * 下载文件到浏览器
     *
     * @author whb
     * @param remotePath
     * @param remoteFileName
     * @return
     * @since JDK 1.8
     */
    public void downloadFile(HttpServletResponse response, String remotePath, String remoteFileName) {
        try (BufferedReader reader = new BufferedReader(new InputStreamReader(sftp.get(remotePath
                + remoteFileName)))) {
            response.setHeader("content-disposition",
                    "attachment;filename=" + URLEncoder.encode(remoteFileName, CHARSET));
            OutputStream out = response.getOutputStream();
            StringBuffer sb = new StringBuffer();
            String buffer = "";
            while ((buffer = reader.readLine()) != null) {
                sb.append(buffer + PROPERTY);
            }
            out.write(sb.toString().getBytes(CHARSET));
            out.flush();
        } catch (SftpException | IOException e) {
            throw new RuntimeException(e);
        }
    }

    /**
     * 批量下载文件
     * @param remotPath：远程下载目录(以路径符号结束,可以为相对路径eg:/home/download/)
     * @param localPath：本地保存目录(以路径符号结束,D:\sftp\)
     * @param fileFormat：下载文件格式(以特定字符开头,为空不做检验)
     * @param fileEndFormat：下载文件格式(文件格式)
     * @param del：下载后是否删除sftp文件
     * @return
     */
    @SuppressWarnings("rawtypes")
    public List<String> batchDownLoadFile(String remotePath, String localPath, String fileFormat,
                                          String fileEndFormat, boolean del) {
        List<String> filenames = new ArrayList<String>();
        Vector v = listFiles(remotePath);
        if (v.size() > 0) {
            System.out.println("本次处理文件个数不为零,开始下载...fileSize=" + v.size());
            Iterator it = v.iterator();
            while (it.hasNext()) {
                LsEntry entry = (LsEntry) it.next();
                String filename = entry.getFilename();
                SftpATTRS attrs = entry.getAttrs();
                if (!attrs.isDir()) {
                    boolean flag = false;
                    String localFileName = localPath + filename;
                    fileFormat = fileFormat == null ? "" : fileFormat.trim();
                    fileEndFormat = fileEndFormat == null ? "" : fileEndFormat.trim();
                    // 三种情况
                    if (fileFormat.length() > 0 && fileEndFormat.length() > 0) {
                        if (filename.startsWith(fileFormat) && filename.endsWith(fileEndFormat)) {
                            flag = downloadFileBoolean(remotePath, filename, localPath, filename);
                            if (flag) {
                                filenames.add(localFileName);
                                if (flag && del) {
                                    deleteSftpFile(remotePath, filename);
                                }
                            }
                        }
                    } else if (fileFormat.length() > 0 && "".equals(fileEndFormat)) {
                        if (filename.startsWith(fileFormat)) {
                            flag = downloadFileBoolean(remotePath, filename, localPath, filename);
                            if (flag) {
                                filenames.add(localFileName);
                                if (flag && del) {
                                    deleteSftpFile(remotePath, filename);
                                }
                            }
                        }
                    } else if (fileEndFormat.length() > 0 && "".equals(fileFormat)) {
                        if (filename.endsWith(fileEndFormat)) {
                            flag = downloadFileBoolean(remotePath, filename, localPath, filename);
                            if (flag) {
                                filenames.add(localFileName);
                                if (flag && del) {
                                    deleteSftpFile(remotePath, filename);
                                }
                            }
                        }
                    } else {
                        flag = downloadFileBoolean(remotePath, filename, localPath, filename);
                        if (flag) {
                            filenames.add(localFileName);
                            if (flag && del) {
                                deleteSftpFile(remotePath, filename);
                            }
                        }
                    }
                }
            }
        }
        return filenames;
    }

    /**
     * 上传文件
     *
     * @author whb
     * @param remotePath
     * @param remoteFileName
     * @param localPath
     * @param localFileName
     * @return
     * @since JDK 1.8
     */
    public void uploadFile(String remotePath, String remoteFileName, String localPath,
                           String localFileName) {
        createDir(remotePath);
        try (InputStream in = new BufferedInputStream(new FileInputStream(new File(localPath
                + localFileName)))) {
            sftp.put(in, remoteFileName);
        } catch (SftpException | IOException e) {
            throw new RuntimeException("上传文件出错");
        }
    }

    /**
     * 上传文件
     *
     * @author whb
     * @param remotePath
     * @param remoteFileName
     * @param localPath
     * @param localFileName
     * @return 返回boolean类型
     * @since JDK 1.8
     */
    public boolean uploadFileBoolean(String remotePath, String remoteFileName, String localPath,
                                     String localFileName) {
        createDir(remotePath);
        try (InputStream in = new BufferedInputStream(new FileInputStream(new File(localPath
                + localFileName)))) {
            sftp.put(in, remoteFileName);
            return true;
        } catch (SftpException | IOException e) {
            return false;
        }
    }

    /**
     * 上传文件
     *
     * @author whb
     * @param in
     * @param remotePath
     * @param remoteFileName
     * @return
     * @since JDK 1.8
     */
    public void uploadFile(InputStream in, String remotePath, String remoteFileName) {
        createDir(remotePath);
        try {
            sftp.put(in, remoteFileName);
        } catch (SftpException e) {
            throw new RuntimeException("上传文件出错");
        }
    }

    /**
     * 批量上传文件
     * @param remotePath：远程保存目录
     * @param localPath：本地上传目录(以路径符号结束)
     * @param del：上传后是否删除本地文件
     * @return
     */
    public boolean bacthUploadFile(String remotePath, String localPath, boolean del) {
        File file = new File(localPath);
        File[] files = file.listFiles();
        for (int i = 0; i < files.length; i++) {
            if (files[i].isFile() && files[i].getName().indexOf("bak") == -1) {
                if (this.uploadFileBoolean(remotePath, files[i].getName(), localPath,
                        files[i].getName())
                        && del) {
                    deleteFile(localPath + files[i].getName());
                }
            }
        }
        return true;
    }

    /**
     * 删除本地文件
     *
     * @author whb
     * @param filePath
     * @return
     * @since JDK 1.8
     */
    public static boolean deleteFile(String filePath) {
        File file = new File(filePath);
        if (!file.exists() || !file.isFile()) {
            return false;
        }
        return file.delete();
    }

    /**
     * 删除stfp文件
     *
     * @author whb
     * @param directory
     *           要删除文件所在目录
     * @param deleteFile
     *           要删除的文件
     * @return
     * @since JDK 1.8
     */
    public void deleteSftpFile(String directory, String deleteFile) {
        try {
            sftp.rm(directory + deleteFile);
        } catch (SftpException e) {
            throw new RuntimeException("删除stfp文件出错");
        }
    }

    /**
     * 创建目录
     *
     * @author whb
     * @param createPath
     * @return
     * @since JDK 1.8
     */
    public boolean createDir(String createPath) {
        try {
            if (isDirExist(createPath)) {
                this.sftp.cd(createPath);
                return true;
            }
            String pathArry[] = createPath.split("/");
            StringBuffer filePath = new StringBuffer("/");
            for (String path : pathArry) {
                if (path.equals("")) {
                    continue;
                }
                filePath.append(path + "/");
                if (isDirExist(filePath.toString())) {
                    sftp.cd(filePath.toString());
                } else {
                    sftp.mkdir(filePath.toString());
                    sftp.cd(filePath.toString());
                }
            }
            this.sftp.cd(createPath);
            return true;

        } catch (Exception e) {
            return false;
        }
    }

    /**
     * 目录是否存在
     *
     * @author whb
     * @param directory
     * @return
     * @since JDK 1.8
     */
    public boolean isDirExist(String directory) {
        boolean flag = false;
        SftpATTRS sftpATTRS;
        try {
            sftpATTRS = sftp.lstat(directory);
            flag = true;
            return sftpATTRS.isDir();
        } catch (Exception e) {
            e.printStackTrace();
            if ("no such file".equals(e.getMessage())) {
                flag = false;
            }
        }
        return flag;
    }

    /**
     * 如果目录不存在就创建目录
     *
     * @author whb
     * @param path
     * @return
     * @since JDK 1.8
     */
    public void mkdirs(String path) {
        File f = new File(path);
        String fs = f.getParent();
        f = new File(fs);
        if (!f.exists()) {
            f.mkdirs();
        }
    }

    /**
     * 列出目录下的文件
     *
     * @author whb
     * @param directory
     *          要列出的目录
     * @return
     * @since JDK 1.8
     */
    @SuppressWarnings("rawtypes")
    public Vector listFiles(String directory) {
        try {
            return sftp.ls(directory);
        } catch (SftpException e) {
            return null;
        }
    }

    public static void main(String[] args) throws IOException {
        //下载linux的文件到本地
        SFTPUtil util = new SFTPUtil("172.23.23.120", "root", "XXXXXXX");
        util.connect();
        util.downloadFile("/home/", "sensitiveword.txt", "E:/", "abc");
        util.disconnect();
        System.out.println("下载成功");

        //上传文件至linux上
        SFTPUtil ftp = new SFTPUtil("172.23.23.120", "root", "XXXXXXX");
        ftp.connect();
        ftp.uploadFile("/home/", "qwe.txt", "E:/", "abc.txt");
        ftp.disconnect();
    }
}


/**
 * Revision history
 * -------------------------------------------------------------------------
 * 
 * Date Author Note
 * -------------------------------------------------------------------------
 * 2017年9月27日 wanghb create
 */